#!/bin/bash
#
# Copyright 2015-2017 Adrian DC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# === Standalone Source Helper ===
# source <(curl -Ls https://github.com/AdrianDC/android_development_shell_tools/raw/master/android_host_grep.rc)
# source <(curl -Ls https://github.com/AdrianDC/android_development_shell_tools/raw/master/android_host_common.rc)
# source <(curl -Ls https://github.com/AdrianDC/android_development_shell_tools/raw/master/android_tools.rc)

# === Binary Editor ===
function binaryeditor()
{
  # Usage
  if [ -z "${1}" ]; then
    echo '';
    echo ' Usage: binaryeditor <patternsearch> [binariespath] [replacement] (Binary files parser and editor)';
    echo '';
    return;
  fi;

  # Variables
  local PATTERN=${1};
  local FILES=${2:-.};
  local REPLACEMENT=${3};
  local REPLACE=;
  local STRINGS=;
  local strings_param;
  PATTERN=${PATTERN//\*/.\*};
  PATTERN=${PATTERN//./.};

  # Handle strings variants
  strings_param="strings $(stringsparams)";

  # Headers
  echo '';
  if [ ! -z "${REPLACEMENT}" ]; then
    echo -e " \e[1;31m=== Replacing '${PATTERN}' with '${REPLACEMENT}' in ${FILES} ===\e[0m";
  else
    echo -e " \e[1;31m=== Searching '${PATTERN}' in ${FILES} ===\e[0m";
  fi;
  echo '';

  # Search files
  for FILE in $(find "${FILES}" -not -path "*.git/*" -not -path "*.repo/*" | sort); do

    # File / directory detection
    FILE=$(readlink -f "${FILE}");
    if [ ! -e "${FILE}" ] || [ -d "${FILE}" ]; then
      continue;
    fi;

    # Replacement mode
    if [ ! -z "${REPLACEMENT}" ]; then
      STRINGS=$(${strings_param} "${FILE}" \
              | grep "${PATTERN}" \
              | sort -u -r);

      # Information found
      if [ ! -z "${STRINGS}" ]; then
        echo -en  "   \e[1;37m${FILE} :\e[0m ${STRINGS} ";
        for OLD_STRING in ${STRINGS}; do
          NEW_STRING=${OLD_STRING//${PATTERN}/${REPLACEMENT}};

          # String conversions
          OLD_STRING_HEX=$(echo -n "${OLD_STRING}" | xxd -g 0 -u -ps -c 256)00;
          NEW_STRING_HEX=$(echo -n "${NEW_STRING}" | xxd -g 0 -u -ps -c 256)00;

          # Dimensions security
          if [ ${#NEW_STRING_HEX} -le ${#OLD_STRING_HEX} ]; then
            while [ ${#NEW_STRING_HEX} -lt ${#OLD_STRING_HEX} ];
            do
              NEW_STRING_HEX=${NEW_STRING_HEX}00;
            done;

            # Modification confirmation
            echo -en  '\e[1;36m[y/N] \e[0m';
            read -r -s -d'' -s -n1 REPLACE;
            if [[ "${REPLACE}" == 'y' || "${REPLACE}" == 'Y' ]]; then
              hexdump -ve '1/1 "%.2X"' "${FILE}" | \
                sed "s/${OLD_STRING_HEX}/${NEW_STRING_HEX}/g" | \
                xxd -r -p > "${FILE}.tmp";
              chmod --reference "${FILE}" "${FILE}.tmp";
              mv "${FILE}.tmp" "${FILE}";
              echo -e '\e[1;32mDone!\e[0m';
            else
              echo -e '\e[1;33mIgnored.\e[0m';
            fi;

          # String overflow
          else
            echo -e '\e[1;36m[N] \e[1;33mString too long...\e[0m';
          fi;
        done;

      # Information not found
      else
        STRINGS=$(${strings_param} "${FILE}" \
                | grep "${REPLACEMENT}" \
                | sort -u -r);
        if [ ! -z "${STRINGS}" ]; then
          echo -e "   \e[1;37m${FILE}:\e[0m ${REPLACEMENT} \e[1;33mFound.\e[0m";
        fi;
      fi;

    # Search mode
    else
      STRINGS=$(${strings_param} "${FILE}" \
              | grep "${PATTERN}" \
              | sort -u -r \
              | tr '\n' ',');
      if [ ! -z "${STRINGS}" ]; then
        echo -e "   \e[1;37m${FILE}:\e[1;33m ${STRINGS}\e[0m";
      fi;
    fi;

  done;
  echo '';
}

# === Binary Search ===
function binarysearch()
{
  # Usage
  if [ -z "${1}" ]; then
    echo '';
    echo ' Usage: binarysearch <patternsearch> [binariespath] (Binary files parser and searcher)';
    echo '';
    return;
  fi;

  # Run binaryeditor in search mode
  binaryeditor "${1}" "${2}";
}

# === Libraries Hunter ===
function librarieshunter()
{
  # Usage
  if [ -z "${1}" ]; then
    echo '';
    echo ' Usage: librarieshunter <binariespath> (Libraries linkage analyzer)';
    echo '';
    return;
  fi;

  # Variables
  local FILES=${1};
  local FILE_NAME=;
  local FILE_REFERENCED=;
  local SYSTEM_PATH=;
  local LIB_ARCH=;
  local LIB_NAME=;
  local DIR_NAMES=;
  local DIR_COMMON_NAMES=;
  local DIR_VENDOR_NAMES=;
  local strings_param;

  # Handle strings variants
  strings_param="strings $(stringsparams)";

  # Scan the libraries
  echo '';
  echo -e " \e[1;31m=== Searching missing libraries in ${FILES} ===\e[0m";
  echo '';
  for FILE in $(find "${FILES}" -not -path "*.git/*" -not -path "*.repo/*" | sort);
  do
    FILE=$(readlink -f "${FILE}");
    if [ ! -e "${FILE}" ] || [ -d "${FILE}" ]; then
      continue;
    fi;

    # Internal references
    STRINGS=$(${strings_param} "${FILE}" \
            | grep "\\.so" \
            | sort -u -r \
            | tr '\n' ',');
    if [ ! -z "${STRINGS}" ]; then
      STRINGS=${STRINGS//,/ };
      echo -e "   \e[1;37m${FILE}:\e[0m";

      # Search missing references
      for LIB in ${STRINGS};
      do
        if [[ ! "${LIB}" == *'lib'*'.so' ]]; then continue; fi;
        if [[ "${LIB}" == *'%'* ]]; then continue; fi;

        LIB_NAME=$(basename "${LIB}");
        SYSTEM_PATH=${FILES%system\/*}system;
        LIB_ARCH=${FILES//vendor\//};
        LIB_ARCH=${LIB_ARCH/*system\/lib};
        LIB_ARCH=${LIB_ARCH%%\/*};
        DIR_COMMON_NAMES=${SYSTEM_PATH}/lib${LIB_ARCH};
        DIR_VENDOR_NAMES=${SYSTEM_PATH}/vendor/lib${LIB_ARCH};
        DIR_NAMES="${DIR_COMMON_NAMES} ${DIR_COMMON_NAMES}/*/ ${DIR_VENDOR_NAMES} ${DIR_VENDOR_NAMES}/*/";
        if [ -z "$(find "${DIR_NAMES}" -name "${LIB_NAME}")" ]; then
          echo -e "    \e[1;31m- Missing reference: \e[1;31m${LIB_NAME}\e[0m";
        fi;
      done;

      # Search unused library
      FILE_NAME=$(basename "${FILE}");
      FILE_REFERENCED=;
      for FILE_SUB in $(find "${FILES}" -not -path "*.git/*" -not -path "*.repo/*" | sort);
      do
        FILE_SUB=$(readlink -f "${FILE_SUB}");
        if [ -d "${FILE_SUB}" ] || [ "${FILE}" = "${FILE_SUB}" ]; then continue; fi;
        STRINGS_SUB=$(${strings_param} "${FILE_SUB}" \
                    | grep "${FILE_NAME}" \
                    | sort -u -r \
                    | tr '\n' ',');
        if [ ! -z "${STRINGS_SUB}" ]; then
          FILE_REFERENCED=true;
          break;
        fi;
      done;
      if [ -z "${FILE_REFERENCED}" ]; then
        echo -e "    \e[1;33m- Unused library: ${FILE_NAME}\e[0m";
      fi;
    fi;
  done;
  echo '';
}

# === Libraries Checker ===
function librarieschecker()
{
  # Usage
  if [ -z "${1}" ]; then
    echo '';
    echo ' Usage: librarieschecker <prebuilts_path> (Unreferenced libraries analyzer)';
    echo '';
    return;
  fi;

  # Variables
  local FILES_PATH=${1};
  local FILE_NAME=;
  local strings_param;

  # Handle strings variants
  strings_param="strings $(stringsparams)";

  # Scan the libraries
  echo '';
  echo -e " \e[1;31m=== Searching unreferenced libraries in ${FILES_PATH} ===\e[0m";
  echo '';
  for FILE in $(find "${FILES_PATH}" -not -path "*.git/*" -not -path "*.repo/*" | sort); do

    # Detect file and allow only library extensions
    FILE=$(readlink -f "${FILE}");
    if [ ! -e "${FILE}" ] || [ -d "${FILE}" ] || [ ! "${FILE: -3}" = '.so' ]; then
      continue;
    fi;

    # Get file name only
    FILE_NAME=$(basename "${FILE}");

    # Present the file being checked
    echo -en "\r\033[K  - Looking for '${FILE_NAME}'...";

    # Search missing references
    if ! grep -aiqr --exclude=\*.mk --exclude="${FILE_NAME}" "${FILE_NAME}" "${FILES_PATH}"; then
      echo -e "\r\033[K  \e[1;31m- Missing reference: \e[1;31m${FILE_NAME}\e[0m";
    fi;

  done;
  echo -e '\r\033[K';
}

# === Hastebin ===
function haste()
{
  # Usage: haste <file> or | haste (Share hastebin logs)

  # Based upon https://github.com/seejohnrun/haste-client

  # Variables
  local tmp;
  local url;

  # Get output
  tmp=$(mktemp);
  if [ ! -z "${1}" ] && [ -f "${1}" ]; then
    tee "${tmp}" < "${1}";
  else
    cat | tee "${tmp}";
  fi;
  echo '';

  # Trim line rewrites
  sed -i 's/\r[^\n]*\r/\r/g' "${tmp}";
  sed -i 's/\(\r\|'$'\033''\[K\)//g' "${tmp}";
  sed -Ei 's/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g' "${tmp}";

  # Upload to hastebin
  url=$(timeout 10 curl -X POST -s --data-binary @"${tmp}" https://hastebin.com/documents \
      | awk -F '"' '{ print "http://hastebin.com/"$4; }');
  echo " haste: ${url}";

  # Open url and delete temp file
  urlopen "${url}";
  rm "${tmp}";
}

# === Pastebin ===
function pbin()
{
  # Usage: pbin <file> or | pbin (Share Pastebin logs)

  # Variables
  local tmp;
  local token;
  local url;

  # Get output
  tmp=$(mktemp);
  if [ ! -z "${1}" ] && [ -f "${1}" ]; then
    tee "${tmp}" < "${1}";
  else
    cat | tee "${tmp}";
  fi;
  echo '';

  # Trim line rewrites
  sed -i 's/\r[^\n]*\r/\r/g' "${tmp}";
  sed -i 's/\(\r\|'$'\033''\[K\)//g' "${tmp}";
  sed -Ei 's/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g' "${tmp}";

  # Acquire token from Pastebin
  token=$(curl -s 'https://pastebin.com' \
        | grep 'csrf_token_post' \
        | sed 's/.*name="csrf_token_post" value="\(.*\)".*/\1/')

  # Upload to Pastebin
  url=$(timeout 10 curl -X POST -s 'https://pastebin.com/post.php' \
        -D - \
        -F "csrf_token_post=${token}" \
        -F "paste_code=<${tmp};type=text/plain" \
        -F "paste_expire_date=${PASTEBIN_EXPIRE:-1D}" \
        -F 'paste_format=8' \
        -F 'paste_private=1' \
        -F 'submit_hidden=submit_hidden' \
      | grep 'location');
  url="http://pastebin.com/raw${url##*: }";
  echo " pastebin: ${url}";

  # Open url and delete temp file
  urlopen "${url}";
  rm "${tmp}";
}

# === Pastebin Permanent ===
alias pbinperm='PASTEBIN_EXPIRE=N pbin';

# === Overlay Compare ===
function overlaycompare()
{
  # Usage
  if [ -z "${1}" ]; then
    echo '';
    echo ' Usage: overlaycompare <overlay_file_path> (Compare device overlays against sources)';
    echo '';
    return;
  fi;

  # Variables
  local source_path;

  # Loop through files
  while IFS= read -r overlay_path; do

    # Launch meld on both paths
    source_path=$(echo "$(gettop)/${overlay_path}" \
                | sed 's/overlay\/common/overlay/' \
                | sed 's/overlay\/dictionaries/overlay/' \
                | sed 's/overlay[^\/]*\///');
    meld "${overlay_path}" "${source_path}";

  done < <(find "${@}" -type f);
}

# === Remote Script Launcher ===
function remotescript()
{
  # Usage
  if [ -z "${1}" ]; then
    echo '';
    echo ' Usage: remotescript <url> [bool_automated] (Remote script launcher with confirmations)';
    echo '';
    return;
  fi;

  # Variables
  local script=${1};
  local line;
  local tmpfile;

  # Header
  echo '';
  echo -e " \e[1;33mremotescript: Importing ${script}...\e[0m";
  echo '';

  # Retrieve script
  tmpfile=$(mktemp);
  curl -Ls "${script}" > "${tmpfile}";

  # Open descriptor 3 on input
  exec 3<"${tmpfile}";

  # Loop through script
  while IFS= read -r -u 3 line; do

    # Handle comment lines
    if echo "${line}" | grep -q '^[ ]*\(#.*\|\)$'; then
      echo -e "\e[1;36m${line}\e[0m";

    # Handle commands lines
    else
      if [ ! -z "${2}" ]; then
        echo -e "\e[1;32m${line}\e[0m";
      else
        echo -en "\e[1;32m${line} \e[1;33m[Enter]\e[0m";
        read -r;
      fi;
      # shellcheck disable=SC2001
      line=$(echo "${line}" | sed 's/;[ ]*$//g');
      ${line};
    fi;

  done;

  # Finish process
  echo '';
  rm -f "${tmpfile}";
  exec 3<&-;
}

# === Work In Progress Shortcuts ===
function wip()
{
  # Usage
  if [ -z "${*}" ] && [ -z "${ANDROID_DEVELOPMENT_SHELL_TOOLS_WIP}" ]; then
    echo '';
    echo ' Usage: wip [commands to store] (Work in progress commands to store and use)';
    echo '';
    return;
  fi;

  # Input given, save commands
  if [ ! -z "${*}" ]; then
    ANDROID_DEVELOPMENT_SHELL_TOOLS_WIP=${*};
    echo '';
    echo -e " \e[1;33mwip: Stored\e[0m '${ANDROID_DEVELOPMENT_SHELL_TOOLS_WIP}'. \e[1;33mUse 'wip' to run the commands\e[0m";
    echo '';

  # No input, run commands
  else
    echo '';
    echo -e " \e[1;33mwip: Running\e[0m '${ANDROID_DEVELOPMENT_SHELL_TOOLS_WIP}'...";
    echo '';
    ${ANDROID_DEVELOPMENT_SHELL_TOOLS_WIP};
  fi;
}
